﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Cfg.Attributes;
using Cfg.ValidationAttributes;

namespace Cfg.Helpers
{
    public static class PropertyInfoExtensions
    {
        public static object[] GetCustomAttributesFromInterfaces(this PropertyInfo propertyInfo)
        {
            var declaringType = propertyInfo.DeclaringType; 
            if (declaringType == null)
                return new object[0];

            var customAttributes = new List<object>();
            foreach (var interfaceType in declaringType.GetInterfaces())
            {
                var interfacePropertyInfo = interfaceType.GetProperty(propertyInfo.Name);
                if (interfacePropertyInfo == null)
                    continue;

                customAttributes.AddRange(interfacePropertyInfo.GetCustomAttributes(false));
            }

            return customAttributes.ToArray();
        }

        public static T GetAttribute<T>(this PropertyInfo propertyInfo) where T : Attribute
        {
            return (T)propertyInfo
                           .GetCustomAttributes(typeof(T), false)
                           .FirstOrDefault();
        }

        /// <summary>
        /// Get properies default value. Returns null if not set
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        public static object GetDefaultValue(this PropertyInfo propertyInfo)
        {
            var defaultAttribute = GetAttribute<DefaultAttribute>(propertyInfo);
            if (defaultAttribute == null)
                return null;

            return defaultAttribute.DefaultValue;
        }

        /// <summary>
        /// Check if field is required in given context
        /// </summary>
        /// <param name="currentContext"></param>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        public static bool IsRequiredIn(this PropertyInfo propertyInfo, string currentContext)
        {
            var requiredAttribute = GetAttribute<RequiredAttribute>(propertyInfo);
            var required = requiredAttribute != null
                           && ((requiredAttribute.Contexts != null && requiredAttribute.Contexts.Contains(currentContext))
                                || (requiredAttribute.Contexts == null)
                           );

            return required;
        }

	    /// <summary>
	    /// Check if field is marked as path
	    /// </summary>
	    /// <param name="propertyInfo"></param>
	    /// <param name="createIfNotExists"></param>
	    /// <param name="throwIfNotExists"></param>
	    /// <returns></returns>
	    public static bool IsPath(this PropertyInfo propertyInfo, out bool createIfNotExists, out bool throwIfNotExists)
        {
            createIfNotExists = false;
	        throwIfNotExists = false;

            var pathAttribute = GetAttribute<PathAttribute>(propertyInfo);
            if (pathAttribute == null)
                return false;

            createIfNotExists = pathAttribute.CreateIfNotExists;
	        throwIfNotExists = pathAttribute.ThrowIfNotExists;

            return true;
        }

		/// <summary>
		/// 
		/// </summary>
		/// <param name="propertyInfo"></param>
		/// <returns></returns>
		public static bool HasCreateDirectoryIfNotExists(this PropertyInfo propertyInfo)
		{
			var createIfNotExists = true;
			var pathAttribute = GetAttribute<PathAttribute>(propertyInfo);
			if (pathAttribute != null)
				createIfNotExists = pathAttribute.CreateIfNotExists;

			return createIfNotExists;
		}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        public static object GetMaxValue(this PropertyInfo propertyInfo)
        {
            var maxAttribute = GetAttribute<MaxAttribute>(propertyInfo);
            if (maxAttribute == null)
                return null;

            return maxAttribute.Max;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        public static object GetMinValue(this PropertyInfo propertyInfo)
        {
            var minAttribute = GetAttribute<MinAttribute>(propertyInfo);
            if (minAttribute == null)
                return null;

            return minAttribute.Min;
        }

        public static string GetMappedKey(this PropertyInfo propertyInfo)
        {
            var attr = GetAttribute<MapAttribute>(propertyInfo);
            if (attr == null)
                return null;

            return attr.Key;
        }
    }
}
